﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Tycho.Runtime {
    public class Multimethod {
        public List<AnyObject> Methods { get; private set; }

        public Multimethod () {
            Methods = new List<AnyObject> ();
        }

        public AnyObject Invoke (params AnyObject [] arguments) {
            Func<AnyObject> method;

            if ((method = DispatchMethod (arguments)) != null) {
                return method ();
            } else {
                throw TychoException.NoMatchingMethodInMultimethod (arguments);
            }
        }

        public virtual Func<AnyObject> DispatchMethod (params AnyObject [] arguments) {
            StackFrameObject frame = new StackFrameObject (RuntimeModule.StackFrame, RuntimeModule.Null);

            foreach (AnyObject method in Methods) {
                if (method.ParametersSchema.Match (frame, arguments)) {
                    if (method is ClosureObject) {
                        ClosureObject closure = method as ClosureObject;
                        frame.OuterScope = closure.Context;
                        return () => closure.InvokeWithFrame (frame);
                    } else if (method is NativeOperation) {
                        return () => (method as NativeOperation).InvokeWithoutSchema (arguments);
                    } else {
                        return () => method.Invoke (arguments);
                    }
                }
            }

            return null;
        }
    }
}
